﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Hook.Graphics;

namespace Hook.Primitives
{
    public class Polygon
    {
        public List<Vector3> Offsets;
        public List<PolyPoint> Points;
        public List<Line> InnerLines;
        public List<Line> OuterLines;
        public int FarthestPointIndex;
        public PolyPoint ReferencePoint;                            //Absolute location of the polygon
        public Vector3 OriginOffset, Rotation, RotationOffset;      //Offset from reference point of the polygon origin

        public Polygon(PolyPoint ReferencePoint, List<Vector3> Offsets)
        {
            this.ReferencePoint = ReferencePoint;
            this.Offsets = Offsets;

            Points = new List<PolyPoint>();
            InnerLines = new List<Line>();
            OuterLines = new List<Line>();

            SetPoints();

            /*foreach(Vector3 v in this.Offsets)
                Points.Add(new PolyPoint(ReferencePoint.Position + OriginOffset + v));
            InnerLines = new List<Line>();
            OuterLines = new List<Line>();
            for(int n = 0; n < Points.Count; n++)
            {
                InnerLines.Add(new Line(new PolyPoint(ReferencePoint.Position + OriginOffset), Points[n]));
            }
            for(int n = 0; n < Points.Count - 1; n++)
            {
                OuterLines.Add(new Line(Points [n], Points[n + 1]));
            }
            if(Points.Count > 0)
                OuterLines.Add(new Line(Points[Points.Count - 1], Points[0]));*/
        }

        /// <summary>
        /// Rotates Offset vector about the Z axis by a given rotation.
        /// </summary>
        public Vector3 RotateOffset(Vector3 Offset, Vector3 Rotation)
        {
            float X = Offset.X * (float)Math.Cos(Rotation.Z) + Offset.Y * -(float)Math.Sin(Rotation.Z);
            float Y = Offset.X * (float)Math.Sin(Rotation.Z) + Offset.Y * (float)Math.Cos(Rotation.Z);
            Offset.X = X;
            Offset.Y = Y;
            return Offset;
        }

        public void SetPoints(List<Vector3> Offsets)
        {
            this.Offsets = Offsets;
            SetPoints();
        }

        public void SetPoints()
        {
            Points.Clear();
            FarthestPointIndex = -1;
            for(int n = 0; n < Offsets.Count; n++)
            {
                Points.Add(new PolyPoint(ReferencePoint.Position + OriginOffset + RotateOffset(Offsets[n], Rotation)));
                if (FarthestPointIndex == -1)
                {
                    FarthestPointIndex = n;
                }
                else if (Offsets[n].Length() > Offsets[FarthestPointIndex].Length())
                {
                    FarthestPointIndex = n;
                }
            }
            InnerLines.Clear();
            OuterLines.Clear();
            for(int n = 0; n < Points.Count; n++)
            {
                InnerLines.Add(new Line(new PolyPoint(ReferencePoint.Position + OriginOffset), Points[n]));
            }
            for(int n = 0; n < Points.Count - 1; n++)
            {
                OuterLines.Add(new Line(Points[n], Points[n + 1]));
            }
            if(Points.Count > 0)
                OuterLines.Add(new Line(Points[Points.Count - 1], Points[0]));
        }

        public void Update(GameTime gt)
        {
            ReferencePoint.Update(gt);
            for(int n = 0; n < Points.Count; n++)
            {
                Points[n].Position = ReferencePoint.Position + RotateOffset(OriginOffset, RotationOffset) + RotateOffset(Offsets[n], Rotation);
                Points[n].Update(gt);
            }

            for(int n = 0; n < InnerLines.Count; n++)
            {
                InnerLines[n].Start = new PolyPoint(ReferencePoint.Position + RotateOffset(OriginOffset, RotationOffset));
                InnerLines[n].End = Points[n];
                InnerLines[n].Update(gt);
            }
            for(int n = 0; n < OuterLines.Count - 1; n++)
            {
                OuterLines[n].Start = Points[n];
                OuterLines[n].End = Points[n + 1];
                OuterLines[n].Update(gt);
            }
            if(OuterLines.Count > 1)
            {
                OuterLines[OuterLines.Count - 1].Start = Points[OuterLines.Count - 1];
                OuterLines[OuterLines.Count - 1].End = Points[0];
                OuterLines[OuterLines.Count - 1].Update(gt);
            }
        }
        public void Draw(SpriteBatch sb, Camera Cam)
        {
            ReferencePoint.Draw(sb, Cam);
            foreach(PolyPoint p in Points)
            {
                p.Draw(sb, Cam);
            }
            foreach(Line l in InnerLines)
            {
                l.Draw(sb, Cam);
            }
            foreach(Line l in OuterLines)
            {
                l.Draw(sb, Cam);
            }
        }

        public List<PolyPoint> CollidesWith(Polygon P)
        {
            List<PolyPoint> CollisionPoints = new List<PolyPoint>();
            for(int l1 = 0; l1 < OuterLines.Count; l1++)
            {
                for(int l2 = 0; l2 < P.OuterLines.Count; l2++)
                {
                    PolyPoint pp = OuterLines[l1].Intersection(P.OuterLines[l2]);
                    if(OuterLines[l1].ContainsPoint(pp) && P.OuterLines[l2].ContainsPoint(pp))
                    {
                        CollisionPoints.Add(pp);
                    }
                }
            }
            return CollisionPoints;
        }

        public List<PolyPoint> CollidesWith(Line L)
        {
            List<PolyPoint> CollisionPoints = new List<PolyPoint>();
            for(int l1 = 0; l1 < OuterLines.Count; l1++)
            {
                PolyPoint pp = OuterLines[l1].Intersection(L);
                if(OuterLines[l1].ContainsPoint(pp) && L.ContainsPoint(pp))
                {
                    CollisionPoints.Add(pp);
                }
            }
            return CollisionPoints;
        }
    }
}
